package physics.body;

import java.awt.Color;
import java.awt.Graphics2D;
import java.util.Vector;
import physics.*;

public abstract class Shape {
	protected PVector position = new PVector(0, 0);
	protected PVector prevPosition = new PVector(0, 0);
	protected PVector velocity = new PVector(0, 0);
	protected PVector prevVelocity = new PVector(0, 0);
	protected PVector force = new PVector(0, 0);
	protected double angularVelo = 0;
	protected double prevAngularVelo = 0;
	protected double torque = 0;
	protected double rotation = 0;
	protected double prevRotation = 0;
	protected double mass = 1;
	protected int touchingCount = 0;
	protected boolean gravity = true;
	protected boolean rotateable = true;
	protected boolean moveable = true;
	protected boolean debug = false;
	protected PMatrix boundingBox;
	protected PVector collisionPoint;
	protected Color color = new Color(0, 100, 0);
	protected double coefficientOfRestitution = 1.0;
	protected PVector centerOfMass = new PVector(0, 0);
	protected PVector normalForce = null;
	protected boolean dirty = false;

	public void setDirty(boolean asdf){
		dirty = asdf;
	}

	public boolean isDirty()
	{
		return dirty;
	}

	public PVector getCollision()
	{
		return collisionPoint;
	}

	public abstract PVector[] getPoints();

	public abstract void collisionOccured();

	public abstract double getOneOverCMMomentOfInnertia();

	public double getCoefficientOfRestitution()
	{
		return coefficientOfRestitution;
	}

	public double getOneOverMass()
	{
		return 1.0 / mass;
	}

	public void setNormalForce(PVector nf)
	{
		normalForce = nf;
	}

	public PVector getNormalForce()
	{
		return normalForce;
	}

	public void draw(Graphics2D g, int height)
	{
		PVector[] pts = this.getPoints();
		g.setColor(color);
		for (int i = 1; i < pts.length; i++) {
			g.drawLine(
					(int) pts[i - 1].getX(),
					height - (int) pts[i - 1].getY(),
					(int) pts[i].getX(),
					height - (int) pts[i].getY());
		}
		g.drawLine((int) pts[pts.length - 1].getX(), height - (int) pts[pts.length - 1].getY(), (int) pts[0].getX(), height - (int) pts[0].getY());

		if (isDebug()) {
			g.setColor(Color.RED);
			// collision point
			g.fillRect(
					(int) collisionPoint.getX(),
					height - (int) collisionPoint.getY(),
					3,
					3);
			// normal force
			g.setColor(Color.ORANGE);
			if (normalForce != null)
				g.drawLine((int) collisionPoint.getX(),
						height - (int) collisionPoint.getY(),
						(int) getNormalForce().getX(),
						height - (int) getNormalForce().getY());

			//bounding box
			g.setColor(Color.RED);
//			g.drawRect(
//					(int) boundingBox.getCol0().getX(),
//					(int) (height - boundingBox.getCol0().getY() - (boundingBox.getCol1().getY() - boundingBox.getCol0().getY())),
//					(int) (boundingBox.getCol1().getX() - boundingBox.getCol0().getX()),
//					(int) ((boundingBox.getCol1().getY() - boundingBox.getCol0().getY())));

			g.drawString("vx " + (int) this.velocity.getX(), (int) position.getX() - 22, height - (int) position.getY() - 12);
			g.drawString("vy " + (int) this.velocity.getY(), (int) position.getX() - 22, height - (int) position.getY());
			g.drawString("m  " + this.getMass(), (int) position.getX() - 22, height - (int) position.getY() + 12);
//			g.drawString("av " + this.getAngularVelo(), (int) position.getX() - 22, height - (int) position.getY() + 24);
		}
	}

	public PMatrix getBoundingBox()
	{
		double minX = Double.MAX_VALUE;
		double minY = Double.MAX_VALUE;
		double maxX = Double.MIN_VALUE;
		double maxY = Double.MIN_VALUE;
		PVector[] pts = getPoints();
		for (int i = 0; i < pts.length; i++) {
			minX = Math.min(minX, pts[i].getX());
			minY = Math.min(minY, pts[i].getY());
			maxX = Math.max(maxX, pts[i].getX());
			maxY = Math.max(maxY, pts[i].getY());
		}
		boundingBox.setCol0(new PVector(minX, minY));
		boundingBox.setCol1(new PVector(maxX, maxY));
//		System.out.println(
//				"min=" + minX + "," + minY +
//				"max=" + maxX + "," + maxY
//				);
		return boundingBox;
	}

	public void setCollisionPoint(PVector cp)
	{
		collisionPoint = cp;
	}

	public void setBoundingBox(PMatrix boundingBox)
	{
		this.boundingBox = boundingBox;
	}

	public double getAngularVelo()
	{
		return angularVelo;
	}

	public void setAngularVelo(double angularVelo)
	{
		prevAngularVelo = this.angularVelo;
		this.angularVelo = angularVelo;
	}

	public void adjustAngularVelo(double angularVelo)
	{
		if (isMoveable()) {
			prevAngularVelo = this.angularVelo;
			this.angularVelo += angularVelo;
		}
	}

	public double getCurrentRotation()
	{
		return rotation;
	}

	public void setRotation(double rotation)
	{
		prevRotation = this.rotation;
		this.rotation = rotation;
	}

	public void adjustRotation(double rotation)
	{
		prevRotation = this.rotation;
		this.rotation += rotation;
	}

	public PVector getForce()
	{
		return force;
	}

	public void setForce(PVector force)
	{
		this.force = force;
	}

	public void adjustForce(PVector delta)
	{
		force.add(delta);
	}

	public boolean isGravity()
	{
		return gravity;
	}

	public void setGravity(boolean gravity)
	{
		this.gravity = gravity;
	}

	public double getMass()
	{
		return mass;
	}

	public void setMass(double mass)
	{
		this.mass = mass;
	}

	public boolean isMoveable()
	{
		return moveable;
	}

	public void setMoveable(boolean moveable)
	{
		this.moveable = moveable;
	}

	public PVector getPosition()
	{
		return position;
	}

	public void setPosition(PVector position)
	{
		prevPosition = this.position;
		this.position = position;
	}

	public void adjustPosition(PVector delta, double scale)
	{
		prevPosition = this.position;

		position.add(delta.getScale(scale));
//		position.x += delta.getX() * scale;
//		position.y += delta.getY() * scale;
	}

	public boolean isRotateable()
	{
		return rotateable;
	}

	public void setRotateable(boolean rotateable)
	{
		this.rotateable = rotateable;
	}

	public boolean isDebug()
	{
		return debug;
	}

	public void setDebug(boolean enabled)
	{
		debug = enabled;
	}

	public double getTorque()
	{
		return torque;
	}

	public void setTorque(double torque)
	{
		this.torque = torque;
	}

	public int getTouchingCount()
	{
		return touchingCount;
	}

	public void setTouchingCount(int touchingCount)
	{
		this.touchingCount = touchingCount;
	}

	public PVector getVelocity()
	{
		return new PVector(velocity.getX(), velocity.getY());
	}

	public void setVelocity(PVector velocity)
	{
		if (isMoveable()) {
			prevVelocity = this.velocity;
			this.velocity.set(velocity);
		}
	}

	public void adjustVelocity(PVector delta)
	{
		if (isMoveable()) {
			prevVelocity = this.velocity;
			velocity.add(delta);
		}
	}

	public void stepBack()
	{
		velocity = prevVelocity;
		position = prevPosition;
		rotation = prevRotation;
		angularVelo = prevAngularVelo;
	}

	public PVector getCenterOfMassLoc()
	{
		return new PVector(centerOfMass.getSum(position));
	}

	public PVector getRadius(){
		return new PVector(collisionPoint.getSub(getCenterOfMassLoc()));
		
	}
}
